package cool.doudou.doudada.iot.codec.core.helper;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import cool.doudou.doudada.iot.codec.core.adapter.ICodecAdapter;
import cool.doudou.doudada.iot.codec.core.adapter.impl.DefaultCodecAdapterImpl;
import cool.doudou.doudada.iot.codec.core.api.BundleApi;
import cool.doudou.doudada.iot.codec.core.entity.BundleInfo;
import cool.doudou.doudada.iot.codec.core.util.FileUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * BundleHelper
 *
 * @author jiangcs
 * @since 2022/09/29
 */
@Slf4j
@AllArgsConstructor
public class BundleHelper {
    private BundleApi bundleApi;
    private String bundlePath;

    private final String configFileName = "config.json";

    /**
     * 验证
     *
     * @param zipFileName   zip文件名
     * @param zipPath       zip文件路径
     * @param manufactureId 厂商ID
     * @param productId     产品ID
     * @return true-成功；false-失败
     */
    public boolean verify(String zipFileName, String zipPath, String manufactureId, String productId) {
        // 父路径
        String parentPath = FileUtil.getParentPath(manufactureId, productId);

        List<String> fileNameList = FileUtil.unzip(zipFileName, zipPath, bundlePath, parentPath);
        if (fileNameList == null || fileNameList.size() == 0) {
            log.error("zip文件[{}]: 解压缩失败", zipFileName);
            return false;
        }
        if (fileNameList.size() < 2) {
            log.error("zip文件[{}]: 包含文件缺失", zipFileName);
            return false;
        }

        String packageFileName = null;
        String jarFileName = null;
        for (String fileName : fileNameList) {
            if (fileName.endsWith(".json")) {
                packageFileName = fileName;
            } else if (fileName.endsWith(".jar")) {
                jarFileName = fileName;
            }
        }
        if (packageFileName == null || jarFileName == null) {
            log.error("zip文件[{}]: 包含文件格式错误 => {}", zipFileName, fileNameList);
            return false;
        }

        String packageSymbolicName, packageJarFileName, packageManufactureId, packageProductId;
        try {
            JSONObject packageObj = FileUtil.getPackage(bundlePath, parentPath, packageFileName);
            packageManufactureId = packageObj.getString("manufactureId");
            if (!packageManufactureId.equals(manufactureId)) {
                log.error("manufactureId match fail: packageManufactureId[{}] != manufactureId[{}]", packageManufactureId, manufactureId);
                return false;
            }
            packageProductId = packageObj.getString("productId");
            if (!packageProductId.equals(productId)) {
                log.error("productId match fail: packageProductId[{}] != productId[{}]", packageProductId, productId);
                return false;
            }
            JSONObject bundleObj = packageObj.getJSONObject("bundle");
            packageSymbolicName = bundleObj.getString("symbolicName");
            packageJarFileName = bundleObj.getString("jarFileName");
        } catch (Exception e) {
            log.error("getPackage exception: ", e);
            return false;
        }

        try {
            if (!jarFileName.equals(packageJarFileName)) {
                log.error("jarFileName match fail: jarFileName[{}] != packageJarFileName[{}]", jarFileName, packageJarFileName);
                return false;
            }
            String bundleSymbolicName = FileUtil.getSymbolicName(bundlePath, parentPath, jarFileName);
            if (!bundleSymbolicName.equals(packageSymbolicName)) {
                log.error("bundleSymbolicName match fail: bundleSymbolicName[{}] != packageSymbolicName[{}]", bundleSymbolicName, packageSymbolicName);
                return false;
            }
            return true;
        } catch (Exception e) {
            log.error("verifyJar exception: ", e);
            return false;
        }
    }

    /**
     * 自动部署
     * <p>
     * 根据配置文件部署Bundle
     *
     * @return bundleId集合
     */
    private List<Long> autoDeploy() {
        // 读取配置文件config.json
        try {
            String configStr = FileUtil.read(bundlePath, configFileName);
            if (StringUtils.hasLength(configStr)) {
                JSONArray configArr = JSONObject.parseObject(configStr, JSONArray.class);
                int size = configArr.size();
                if (size > 0) {
                    List<Long> bundleIdList = new ArrayList<>();
                    JSONObject configObj;
                    for (int i = 0; i < size; i++) {
                        configObj = configArr.getJSONObject(i);
                        String fileName = configObj.getString("fileName");
                        String manufactureId = configObj.getString("manufactureId");
                        String productId = configObj.getString("productId");

                        // 部署
                        bundleIdList.add(deploy(fileName, manufactureId, productId));
                    }
                    return bundleIdList;
                }
            }
        } catch (Exception e) {
            log.error("read config.json exception: ", e);
        }
        return null;
    }

    /**
     * 部署
     * <p>
     * Bundle启动成功，写入配置文件
     *
     * @param fileName      Bundle文件名
     * @param manufactureId 厂商ID
     * @param productId     产品ID
     * @return bundleId-成功；null-失败
     */
    public Long deploy(String fileName, String manufactureId, String productId) {
        Long bundleId;

        // 父路径
        String parentPath = FileUtil.getParentPath(manufactureId, productId);

        // 安装
        try {
            bundleId = bundleApi.install(bundlePath, parentPath, fileName);
        } catch (BundleException e) {
            log.error("fileName[{}] install exception: ", fileName, e);
            return null;
        }

        // 启动
        try {
            bundleApi.start(bundleId);
        } catch (BundleException e) {
            log.error("bundleId[{}] start exception: ", bundleId, e);
            return null;
        }

        // 更新配置文件config.json
        try {
            JSONArray configArr;
            // 读取配置文件
            String configStr = FileUtil.read(bundlePath, configFileName);
            if (StringUtils.hasLength(configStr)) {
                configArr = JSONObject.parseObject(configStr, JSONArray.class);
            } else {
                configArr = new JSONArray();
            }
            JSONObject configObj = new JSONObject();
            configObj.put("fileName", fileName);
            configObj.put("manufactureId", manufactureId);
            configObj.put("productId", productId);
            configObj.put("bundleId", bundleId);
            configArr.add(configObj);

            // 写入
            FileUtil.write(bundlePath, configFileName, configArr.toString());
        } catch (Exception e) {
            log.error("write config.json exception: ", e);
        }

        log.info("fileName[{}] deploy ok: {}", fileName, bundleId);
        return bundleId;
    }

    /**
     * 取消部署
     *
     * @param bundleId BundleID
     * @return true-成功；false-失败
     */
    public boolean undeploy(Long bundleId) {
        // 停止
        try {
            bundleApi.stop(bundleId);
        } catch (BundleException e) {
            log.error("bundleId[{}] stop exception: ", bundleId, e);
            return false;
        }

        // 卸载
        try {
            bundleApi.uninstall(bundleId);
        } catch (BundleException e) {
            log.error("bundleId[{}] uninstall exception: ", bundleId, e);
            return false;
        }

        // 更新配置文件config.json
        try {
            JSONArray configArr;
            // 读取配置文件
            String configStr = FileUtil.read(bundlePath, configFileName);
            if (StringUtils.hasLength(configStr)) {
                configArr = JSONObject.parseObject(configStr, JSONArray.class);
                JSONObject configObj;
                for (int i = 0, size = configArr.size(); i < size; i++) {
                    configObj = configArr.getJSONObject(i);
                    if (bundleId == configObj.getLongValue("bundleId")) {
                        configArr.remove(configObj);
                        break;
                    }
                }

                // 写入
                FileUtil.write(bundlePath, configFileName, configArr.toString());
            }
        } catch (Exception e) {
            log.error("write config.json exception: ", e);
        }

        log.info("bundleId[{}] undeploy ok", bundleId);
        return true;
    }

    /**
     * 获取 编解码适配器
     *
     * @param manufactureId 厂商ID
     * @param productId     产品ID
     * @return 编解码适配器
     */
    public ICodecAdapter getCodecAdapter(String manufactureId, String productId) {
        ServiceReference<?>[] serviceReferences = bundleApi.getAssignableService(manufactureId, productId);
        if (serviceReferences == null || serviceReferences.length == 0) {
            return new DefaultCodecAdapterImpl();
        }
        return bundleApi.getCodecAdapter(serviceReferences[0]);
    }

    /**
     * 查询 Bundle 列表
     *
     * @param manufactureId 厂商ID
     * @param productId     产品ID
     * @return BundleInfo集合
     */
    public List<BundleInfo> findBundleList(String manufactureId, String productId) {
        List<BundleInfo> bundleInfoList = new ArrayList<>();
        ServiceReference<?>[] serviceReferences = bundleApi.getAssignableService(manufactureId, productId);
        if (serviceReferences != null && serviceReferences.length > 0) {
            Arrays.asList(serviceReferences).forEach(serviceReference -> {
                Bundle bundle = serviceReference.getBundle();

                BundleInfo bundleInfo = new BundleInfo();
                bundleInfo.setId(bundle.getBundleId());
                bundleInfo.setSymbolicName(bundle.getSymbolicName());
                bundleInfo.setState(bundle.getState());
                bundleInfo.setLocation(bundle.getLocation());
                bundleInfo.setVersion(bundle.getVersion());
                bundleInfo.setManufactureId(String.valueOf(serviceReference.getProperty("manufactureId")));
                bundleInfo.setProductId(String.valueOf(serviceReference.getProperty("productId")));
                bundleInfo.setLastModified(bundle.getLastModified());
                bundleInfoList.add(bundleInfo);
            });
        }
        return bundleInfoList;
    }
}
